home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 11 / Mac Magazin and MacEasy Magazine CD - Issue 11.iso / Sharewarebibliothek / Entwickler / WASTE 1.1 C / WEDrawing.c < prev    next >
Text File  |  1995-05-20  |  30KB  |  1,057 lines

  1. // WEDrawing.c
  2. /*
  3. { WASTE PROJECT: }
  4. { Drawing routines and other basic support functions }
  5.  
  6. { Copyright © 1993-1995 Marco Piovanelli }
  7. { All Rights Reserved }
  8. */
  9.  
  10. #include "WASTEIntf.h"
  11. #include <Palettes.h>
  12. #include <QDOffscreen.h>
  13.  
  14. // { If WASTE_RESOLVE_FONT_DESIGNATORS is TRUE, font designators (the special }
  15. // { IDs 0 and 1 that identify the system and the application font, respectively) }
  16. // { are mapped by _WECopyStyle to the actual font IDs }
  17.  
  18. #define WASTE_RESOLVE_FONT_DESIGNATORS    true
  19.  
  20. pascal long WEOffsetToLine (long offset, WEHandle hWE)
  21. {
  22.     //{ given a byte offset into the text, find the corresponding line index }
  23.  
  24.     WEPtr pWE;
  25.     LineArrayPtr pLines;
  26.     long minIndex, maxIndex, index;
  27.     
  28.     pWE = *hWE;
  29.  
  30.     //{ get a pointer to the line array }
  31.     pLines = *pWE->hLines;
  32.  
  33.     //{ do a fast binary search through the style run array }
  34.     minIndex = 0;
  35.     maxIndex = pWE->nLines;
  36.  
  37.     while (minIndex < maxIndex)
  38.     {
  39.         index = BSR(minIndex + maxIndex, 1);
  40.         if (offset >= pLines[index].lineStart) 
  41.         {
  42.             if (offset < pLines[index + 1].lineStart) 
  43.             {
  44.                 break;
  45.             }
  46.             else
  47.             {
  48.                 minIndex = index + 1;
  49.             }
  50.         }
  51.         else
  52.         {
  53.             maxIndex = index;
  54.         }
  55.     }  //{ while }
  56.  
  57.     return index;
  58. }
  59.  
  60. pascal long _WEPixelToLine(long vOffset, WEHandle hWE)
  61. {
  62.     //{ given a vertical pixel offset in local coordinates, }
  63.     //{ find the corresponding line index }
  64.  
  65.     WEPtr pWE;
  66.     LineArrayPtr pLines;
  67.     long minIndex, maxIndex, index;
  68.     
  69.     pWE = *hWE;
  70.  
  71.     //{ get a pointer to the line array }
  72.     pLines = *pWE->hLines;
  73.  
  74.     //{ do a fast binary search through the style run array }
  75.     minIndex = 0;
  76.     maxIndex = pWE->nLines;
  77.  
  78.     while (minIndex < maxIndex)
  79.     {
  80.         index = BSR(minIndex + maxIndex, 1);
  81.         if (vOffset >= pLines[index].lineOrigin) 
  82.         {
  83.             if (vOffset < pLines[index + 1].lineOrigin) 
  84.             {
  85.                 break;
  86.             }
  87.             else
  88.             {
  89.                 minIndex = index + 1;
  90.             }
  91.         }
  92.         else
  93.         {
  94.             maxIndex = index;
  95.         }
  96.     }
  97.     
  98.     return index;
  99. }
  100.  
  101. pascal long _WEOffsetToRun (long offset, WEHandle hWE)
  102. {
  103.     WEPtr pWE;
  104.     RunArrayPtr pRuns;
  105.     long minIndex, maxIndex, index;
  106.  
  107.     pWE = *hWE;
  108.  
  109.     //{ get a pointer to the style run array }
  110.     pRuns = *pWE->hRuns;
  111.  
  112.     //{ do a fast binary search through the style run array }
  113.     minIndex = 0;
  114.     maxIndex = pWE->nRuns;
  115.  
  116.     while (minIndex < maxIndex)
  117.     {
  118.         index = BSR(minIndex + maxIndex, 1);
  119.         if (offset >= pRuns[index].runStart)
  120.         { 
  121.             if (offset < pRuns[index + 1].runStart) 
  122.             {
  123.                 break;
  124.             }
  125.             else
  126.             {
  127.                 minIndex = index + 1;
  128.             }
  129.         }
  130.         else
  131.         {
  132.             maxIndex = index;
  133.         }
  134.     } //{ while }
  135.     return index;
  136. }
  137.  
  138. pascal void _WEGetIndStyle(long runIndex, WERunInfo *info, WEHandle hWE)
  139. {
  140.     WEPtr pWE;
  141.     RunArrayPeek pTheRun;
  142.  
  143.     pWE = *hWE;
  144.  
  145.     //{ get a pointer to the specified run array element }
  146.     pTheRun = (RunArrayPeek)&(*pWE->hRuns)[runIndex];
  147.  
  148.     //{ fill in the runStart and runEnd fields from the style run array }
  149.     info->runStart = pTheRun->first.runStart;
  150.     info->runEnd = pTheRun->second.runStart;
  151.  
  152.     //{ copy the style information from the appropriate entry in the style table }
  153.     info->runAttrs = (*pWE->hStyles)[pTheRun->first.styleIndex].info;
  154. }
  155.  
  156. pascal void WEGetRunInfo(long offset, WERunInfo *info, WEHandle hWE)
  157. {
  158.     _WEGetIndStyle(_WEOffsetToRun(offset, hWE), info, hWE);
  159. }
  160.  
  161. pascal OSErr WEGetSelectedObject(WEObjectDescHandle *hObjectDesc, WEHandle hWE)
  162. {
  163.     WEPtr pWE;
  164.     WERunInfo runInfo;
  165.  
  166.     // { assume current selection is not an embedded object }
  167.     *hObjectDesc = nil;
  168.  
  169.     // { check selection range }
  170.     pWE = *hWE;
  171.     if (pWE->selEnd - pWE->selStart == 1)
  172.     {
  173.     
  174.         // { check run info }
  175.         WEGetRunInfo(pWE->selStart, &runInfo, hWE);
  176.         *hObjectDesc = (WEObjectDescHandle)(runInfo.runAttrs.runStyle.tsObject);
  177.         if (*hObjectDesc != nil)
  178.             return noErr;
  179.     }
  180.     return weObjectNotFoundErr;
  181. } // { WEGetSelectedObject }
  182.  
  183. pascal long WEFindNextObject(long offset, WEObjectDescHandle *hObjectDesc, WEHandle hWE)
  184. {
  185.     StyleTablePtr pStyles;
  186.     RunArrayElementPtr pRun;
  187.     long obj;
  188.     long retval;
  189.     
  190.     retval = kInvalidOffset;
  191.     obj = kNullObject;
  192.     pStyles = *(*hWE)->hStyles;
  193.  
  194.     // { get a pointer to the run array element immediately following offset }
  195.     pRun = &(*(*hWE)->hRuns)[_WEOffsetToRun(offset + 1, hWE)];
  196.  
  197.     // { perform a fast linear scan of the run array looking for a run whose }
  198.     // { corresponding style table entry points to an embedded object; }
  199.     // { the search will stop anyway because the last run array element has styleIndex = -1 }
  200.     while (pRun->styleIndex >= 0)
  201.     {
  202.         obj = pStyles[pRun->styleIndex].info.runStyle.tsObject;
  203.         if (obj != kNullObject)
  204.         {
  205.             retval = pRun->runStart;
  206.             break;
  207.         }
  208.         pRun++;
  209.     } // { while }
  210.     *hObjectDesc = (WEObjectDescHandle)(obj);
  211.     
  212.     return retval;
  213. } // { WEFindNextObject }
  214.  
  215.  
  216. pascal void _WEContinuousStyleRange(long rangeStart, long rangeEnd, short *mode,
  217.         WETextStyle *ts, WEHandle hWE)
  218. {
  219.     //{ find out which style attributes are continous over the specified text range }
  220.     //{ on entry, the mode bitmap specifies which attributes are to be checked }
  221.     //{ on exit, the mode bitmap specifies the continuous attributes, also copied to ts }
  222.  
  223.     WEPtr pWE;
  224.     long bitmap;
  225.     long runIndex;
  226.     WERunInfo runInfo;
  227.     
  228.     pWE = *hWE;
  229.  
  230.     // { get bitmap of style attributes to check (masking out private and unused bits) }
  231.     bitmap = *mode & weDoAll;
  232.  
  233.     //{ get style info at the beginning of the specified range }
  234.     runIndex = _WEOffsetToRun(rangeStart, hWE);
  235.     _WEGetIndStyle(runIndex, &runInfo, hWE);
  236.  
  237.     //{ copy the specified fields to ts }
  238.     _WECopyStyle(&runInfo.runAttrs.runStyle, (WETextStyle *)ts, 0, bitmap | weDoReplaceFace);
  239.  
  240.     //{ loop through style runs across the current selection range }
  241.     //{ if we determine that all specified attributes are discontinuous, we exit prematurely }
  242.     do
  243.     {
  244.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  245.  
  246.         //{ determine which attributes have changed, if any }
  247.         if (BTST(bitmap, kModeFont))
  248.         { 
  249.             if (runInfo.runAttrs.runStyle.tsFont != ts->tsFont)
  250.             { 
  251.                     BCLR(bitmap, kModeFont);
  252.             }
  253.         }
  254.         if (BTST(bitmap, kModeFace))
  255.         { 
  256.             if (runInfo.runAttrs.runStyle.tsFace != ts->tsFace)
  257.             { 
  258.                 ts->tsFace = ts->tsFace & runInfo.runAttrs.runStyle.tsFace;
  259.                 if (ts->tsFace == 0)
  260.                 { 
  261.                     BCLR(bitmap, kModeFace);
  262.                 }
  263.             }
  264.         }
  265.         if (BTST(bitmap, kModeSize))
  266.         { 
  267.             if (runInfo.runAttrs.runStyle.tsSize != ts->tsSize) 
  268.             {
  269.                 BCLR(bitmap, kModeSize);
  270.             }
  271.         }
  272.         if (BTST(bitmap, kModeColor))
  273.         { 
  274.             if (!_WEBlockCmp((Ptr)&runInfo.runAttrs.runStyle.tsColor, (Ptr)&ts->tsColor, sizeof(RGBColor)))
  275.             { 
  276.                 BCLR(bitmap, kModeColor);
  277.             }
  278.         }
  279.  
  280.         runIndex = runIndex + 1;
  281.     } while ((bitmap != 0) && (runInfo.runEnd < rangeEnd));
  282.  
  283.     *mode = bitmap;
  284. }
  285.  
  286. pascal void _WESynchNullStyle(WEHandle hWE)
  287. {
  288.     //{ This routine fills the nullStyle field of the WE record with valid information }
  289.     //{ and makes sure that the null style font belongs to the keyboard script. }
  290.  
  291.     WEPtr pWE;
  292.     long runIndex;
  293. #ifndef WASTENOSYNCH
  294.     ScriptCode keyboardScript;
  295.     short fontID;
  296. #endif
  297.     WERunInfo runInfo;
  298.     
  299.     pWE = *hWE;    // { the WE record must be already locked }
  300.  
  301.     //{ find the run index of the style run preceding the insertion point }
  302.     runIndex = _WEOffsetToRun(pWE->selStart - 1, hWE);
  303.  
  304.     //{ if the nullStyle record is marked as invalid, fill it with the style attributes }
  305.     //{ associated with the character preceding the insertion point, and mark it as valid }
  306.     if (!BTST(pWE->flags, weFUseNullStyle)) 
  307.     {
  308.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  309.         pWE->nullStyle = runInfo.runAttrs;
  310.         BSET(pWE->flags, weFUseNullStyle);
  311.     }
  312.  
  313. #ifndef WASTENOSYNCH
  314.     //{ if only the Roman script is installed, we're finished }
  315.     if (!BTST(pWE->flags, weFNonRoman)) 
  316.     {
  317.         return;
  318.     }
  319.  
  320.     //{ *** FONT / KEYBOARD SYNCHRONIZATION *** }
  321.     //{ get the keyboard script }
  322.     keyboardScript = GetScriptManagerVariable(smKeyScript);
  323.  
  324.     //{ find out what font will be used for the next character typed }
  325.     fontID = pWE->nullStyle.runStyle.tsFont;
  326.  
  327.     //{ do nothing if the font script is the same as the keyboard script }
  328.     if (FontToScript(fontID) == keyboardScript) return; 
  329.  
  330.     //{ scan style runs starting from the insertion point backwards,}
  331.     //{ looking for the first font belonging to the keyboard script }
  332.     do
  333.     {
  334.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  335.         fontID = runInfo.runAttrs.runStyle.tsFont;
  336.         if (FontToScript(fontID) == keyboardScript) break;
  337.         runIndex = runIndex - 1;
  338.     } while (runIndex>=0);
  339.     
  340.     //{ if no font was ever used for the keyboard script, default to the }
  341.     //{ application font for the script }
  342.     if (runIndex < 0) 
  343.     {
  344.         fontID = GetScriptVariable(keyboardScript, smScriptAppFond);
  345.     }
  346.     
  347.     //{ change the font in the null style record }
  348.     pWE->nullStyle.runStyle.tsFont = fontID;
  349. #endif
  350. }
  351.  
  352. pascal Boolean WEContinuousStyle (short *mode, TextStyle *ts, WEHandle hWE)
  353. {
  354.     //{ find out which style attributes are continous over the selection range }
  355.     //{ on entry, the mode bitmap specifies which attributes are to be checked }
  356.     //{ on exit, the mode bitmap specifies the continuous attributes, also copied to ts }
  357.     //{ return TRUE if all specified attributes are continuous }
  358.  
  359.     WEPtr pWE;
  360.     short oldMode;
  361.     Boolean continuousStyle;
  362.     Boolean saveWELock;
  363.  
  364.     // { lock the WE record }
  365.     
  366.     pWE = *hWE;
  367.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  368.  
  369.     // { mask out private and unused bits in mode so we don't run the risk of overwriting }
  370.     // { memory past the ts record (which is defined as TextStyle in the public interfaces) }
  371.     *mode = *mode & weDoAll;
  372.  
  373.     //{ two rather different paths are taken depending on whether }
  374.     //{ the selection range is empty or not }
  375.     if (pWE->selStart == pWE->selEnd) 
  376.     {
  377.         //{ if the selection range is empty, always return TRUE and set ts }
  378.         //{ from the nullStyle record, after having validated it }
  379.         continuousStyle = true;
  380.         _WESynchNullStyle(hWE);
  381.         _WECopyStyle(&pWE->nullStyle.runStyle, (WETextStyle *)ts, 0, *mode | weDoReplaceFace);
  382.     }
  383.     else
  384.     {
  385.         //{ otherwise get the continuous style attributes over the selection range }
  386.         oldMode = *mode;
  387.         _WEContinuousStyleRange(pWE->selStart, pWE->selEnd, mode, (WETextStyle *)ts, hWE);
  388.  
  389.         //{ return TRUE if mode hasn't changed }
  390.         continuousStyle = (oldMode == *mode);
  391.     }
  392.  
  393.     // { unlock the WE record }
  394.     _WESetHandleLock((Handle)hWE, saveWELock);    
  395.  
  396.     return continuousStyle;
  397. }
  398.  
  399. pascal void _WESegmentLoop(long firstLine, long lastLine, SegmentLoopProcPtr callback, void *callbackData,
  400.         WEHandle hWE)
  401. {
  402.     //{ For each style segment on every line in the specified range, set up }
  403.     //{ text attributes in the port and call the callback. }
  404.     //{ the WE record must be already locked }
  405.  
  406.     WEPtr pWE;
  407.     LineArrayPtr pLines;
  408.     long pText;
  409.     long lineIndex;
  410.     long runIndex, previousRunIndex;
  411.     long lineStart, lineEnd, segmentStart, segmentEnd;
  412.     JustStyleCode styleRunPosition;
  413.     WERunInfo runInfo;
  414.     Boolean saveLineLock;
  415.     Boolean saveTextLock;
  416.     QDEnvironment saveEnvironment;
  417.     
  418.     pWE = *hWE;
  419.  
  420.     //{ save the QuickDraw environment }
  421.     _WESaveQDEnvironment(pWE->port, BTST(pWE->flags, weFHasColorQD), &saveEnvironment);
  422.  
  423.     //{ make sure firstLine and lastLine are within the allowed range }
  424.     lineIndex = pWE->nLines - 1;
  425.     firstLine = _WEPinInRange(firstLine, 0, lineIndex);
  426.     lastLine = _WEPinInRange(lastLine, 0, lineIndex);
  427.  
  428.     //{ lock the line array }
  429.     saveLineLock = _WESetHandleLock((Handle)pWE->hLines, true);
  430.     pLines = *pWE->hLines;
  431.  
  432.     //{ lock the text }
  433.     saveTextLock = _WESetHandleLock(pWE->hText, true);
  434.     pText = (long)(*pWE->hText);
  435.  
  436.     //{ find the style run index corresponding to the beginning of the first line }
  437.     runIndex = _WEOffsetToRun(pLines[firstLine].lineStart, hWE);
  438.     previousRunIndex = -1;
  439.  
  440.     //{ loop thru the specified lines }
  441.     for(lineIndex = firstLine; lineIndex<=lastLine; lineIndex++)
  442.     {
  443.         //{ get line start and line end }
  444.         lineStart = pLines[lineIndex].lineStart;
  445.         lineEnd = pLines[lineIndex + 1].lineStart;
  446.  
  447.         //{ loop thru each style run on this line }
  448.         do
  449.         {
  450.             //{ get style run information for the current style run }
  451.             _WEGetIndStyle(runIndex, &runInfo, hWE);
  452.  
  453.             if (previousRunIndex != runIndex) 
  454.             {
  455.                 //{ set new text attributes }
  456.                 TextFont(runInfo.runAttrs.runStyle.tsFont);
  457.                 TextFace(runInfo.runAttrs.runStyle.tsFace);
  458.                 TextSize(runInfo.runAttrs.runStyle.tsSize);
  459.  
  460.                 //{ remember previous run index }
  461.                 previousRunIndex = runIndex;
  462.             }
  463.  
  464.             //{ determine the relative position of this style run on the line }
  465.             styleRunPosition = 0;                                        //{ onlyStyleRun }
  466.  
  467.             if (runInfo.runStart <= lineStart)
  468.             { 
  469.                 segmentStart = lineStart;
  470.             }
  471.             else
  472.             {
  473.                 styleRunPosition = styleRunPosition + 2;    //{ rightStyleRun or middleStyleRun }
  474.                 segmentStart = runInfo.runStart;
  475.             }
  476.  
  477.             if (runInfo.runEnd >= lineEnd)
  478.             {
  479.                         segmentEnd = lineEnd;
  480.             }
  481.             else
  482.             {
  483.                 styleRunPosition = styleRunPosition + 1;    //{ leftStyleRun or middleStyleRun }
  484.                 segmentEnd = runInfo.runEnd;
  485.             }
  486.  
  487.             //{ do the callback }
  488.             if ((callback)(&pLines[lineIndex], &runInfo.runAttrs, (Ptr)(pText + segmentStart),
  489.                     segmentStart, segmentEnd - segmentStart, styleRunPosition, callbackData))
  490.             {
  491.                 break;
  492.             }
  493.  
  494.             //{ advance style run index, unless this style run goes on to the next line }
  495.             if (runInfo.runEnd <= lineEnd)
  496.             { 
  497.                 runIndex = runIndex + 1;
  498.             }
  499.         } while (runInfo.runEnd < lineEnd);
  500.     }  //{ for }
  501.  
  502.     //{ unlock the text }
  503.     _WESetHandleLock((Handle)pWE->hText, saveTextLock);    
  504.  
  505.     //{ unlock the line array }
  506.     _WESetHandleLock((Handle)pWE->hLines, saveLineLock);
  507.  
  508.     //{ restore the QuickDraw environment }
  509.     _WERestoreQDEnvironment(&saveEnvironment);
  510. }
  511.  
  512. pascal void _WEDrawTSMHilite(Rect *segmentRect, short tsFlags)
  513. {
  514.     long flags;
  515.     short underlineHeight;
  516.     RGBColor background, foreground, saveForeground;
  517.     Boolean isColorPort;
  518.     Boolean usingTrueGray;
  519.  
  520.     flags = tsFlags;
  521.     isColorPort = (((CGrafPtr)(qd.thePort))->portVersion < 0);
  522.     usingTrueGray = false;
  523.  
  524.     //{ by default, the pen pattern is solid }
  525.     PenPat(&qd.black);
  526.  
  527.     //{ if we're drawing in color, set the foreground color }
  528.     if (isColorPort) 
  529.     {
  530.         //{ save foreground color }
  531.         GetForeColor(&saveForeground);
  532.  
  533.         //{ by default, the foreground color is black }
  534.         foreground.red = 0;
  535.         foreground.green = 0;
  536.         foreground.blue = 0;
  537.  
  538.         //{ if we're underlining raw (unconverted) text, see if a "true gray" is available }
  539.         if (!BTST(flags, tsTSMConverted)) 
  540.         {
  541.             GetBackColor(&background);
  542.             usingTrueGray = GetGray(GetGDevice(), &background, &foreground);
  543.         } //{ if raw text }
  544.  
  545.         //{ set the foreground color }
  546.         RGBForeColor(&foreground);
  547.     } //{ if color graf port }
  548.  
  549.     //{ if we're underlining raw (unconverted) text and no true gray is available, }
  550.     //{ simulate gray with a 50% pattern }
  551.     if (!BTST(flags, tsTSMConverted)) 
  552.     {
  553.         if (usingTrueGray == false)
  554.         { 
  555.                 PenPat(&qd.gray);
  556.         }
  557.     }
  558.     //{ use a 2-pixel tall underline if text is "selected", else use a 1-pixel tall underline }
  559.     if( BTST(flags, tsTSMSelected)) 
  560.     {
  561.         underlineHeight = 2;
  562.     }
  563.     else
  564.     {
  565.         underlineHeight = 1;
  566.     }
  567.     
  568.     //{ segmentRect becomes the rectangle to paint }
  569.     InsetRect(segmentRect, 1, 0);
  570.     segmentRect->top = segmentRect->bottom - underlineHeight;
  571.  
  572.     //{ draw the underline }
  573.     PaintRect(segmentRect);
  574.  
  575.     //{ restore the foreground color }
  576.     if (isColorPort) 
  577.     {
  578.         RGBForeColor(&saveForeground);
  579.     }
  580. }
  581.  
  582. Boolean SLDraw (LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
  583.         long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
  584.         void *callbackData);
  585.  
  586. // typedef BitMap *BitMapPtr; removed in 1.0
  587.  
  588. Boolean SLDraw (LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
  589.         long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
  590.         void *callbackData)
  591. {
  592. #pragma unused(segmentStart)
  593.     struct SLDrawData *p = (struct SLDrawData *) callbackData;
  594.     WEPtr pWE = *p->hWE;
  595.     Fixed slop;
  596.     Rect segmentRect;
  597.     RGBColor theColorBlack;
  598.     Boolean retval;
  599.  
  600.     retval = false;                            //{ keep looping }
  601.  
  602.     //{ is this the first segment on this line? }
  603.     if (styleRunPosition <= leftStyleRun) 
  604.     {
  605.         //{ calculate the line rectangle (the rectangle which completely encloses the current line) }
  606.         (p->lineRect).left = pWE->destRect.left;
  607.         (p->lineRect).right = pWE->destRect.right;
  608.         (p->lineRect).top = pWE->destRect.top + pLine->lineOrigin;
  609.         (p->lineRect).bottom = pWE->destRect.top + ((LinePeek)pLine)->second.lineOrigin;
  610.  
  611.         //{ calculate the visible portion of this rectangle }
  612.         //{ we do this by intersecting the line rectangle with the view rectangle }
  613.         (p->drawRect) = (*pWE->viewRgn)->rgnBBox;
  614.         if (SectRect(&(p->lineRect), &(p->drawRect), &(p->drawRect)))
  615.         { 
  616.             ;
  617.         }
  618.         if (p->usingOffscreen) 
  619.         {
  620.             //{ calculate the boundary rectangle for the offscreen buffer }
  621.             //{ this is simply drawRect converted to global coordinates }
  622.             p->bounds = (p->drawRect);
  623.             LocalToGlobal((Point *)&p->bounds.top);
  624.             LocalToGlobal((Point *)&p->bounds.bottom);
  625.  
  626.             //{ update the offscreen graphics world for the new bounds (this could fail) }
  627.             p->drawingOffscreen = false;
  628.             if (UpdateGWorld((GWorldPtr *)(&pWE->offscreenPort), 0, &p->bounds, (CTabHandle)nil,
  629.                 (GDHandle)nil, (GWorldFlags)0) >= 0) 
  630.             {
  631.                 //{ NOTE: when running on a 68000 machine with the original QuickDraw, }
  632.                 //{ a GWorld is just an extended GrafPort, and GetGWorldPixMap actually }
  633.                 //{ returns a handle to a _copy_ of the GrafPort portBits (a BitMap, not a PixMap). }
  634.                 //{ An important side-effect of this is that when we call SetOrigin, }
  635.                 //{ only the original portBits is offset, not the copy. }
  636.                 //{ get the pixel map associated with the offscreen graphics world }
  637.                 p->offscreenPixels = GetGWorldPixMap((GWorldPtr)(pWE->offscreenPort));
  638.  
  639.                 //{ lock it down }
  640.                 if (LockPixels(p->offscreenPixels)) 
  641.                 {
  642.                     //{ offscreen pixel buffer allocation was successful }
  643.                     p->drawingOffscreen = true;
  644.  
  645.                     //{ switch graphics world }
  646.                     SetGWorld((GWorldPtr)(pWE->offscreenPort), nil);
  647.  
  648.                     //{ synchronize the coordinate system of the offscreen port with that of the screen port }
  649.                     SetOrigin(p->drawRect.left, p->drawRect.top);
  650.  
  651.                     //{ reset the offscreen clip region }
  652.                     ClipRect(&p->drawRect);
  653.                 }
  654.             } //{ if pixel buffer allocation was successful }
  655.         } //{ if usingOffscreen }
  656.  
  657.         //{ if doErase is TRUE, erase the drawable area before drawing text }
  658.         if (p->doErase) 
  659.         {
  660.             EraseRect(&p->drawRect);
  661.         }
  662.         //{ position the pen }
  663.         MoveTo(p->lineRect.left + _WECalcPenIndent(pLine->lineSlop, pWE->alignment),
  664.             p->lineRect.top + pLine->lineAscent);
  665.     } //{ if first segment on line }
  666.  
  667.     //{ if drawingOffscreen, switch thePort to the offscreen port }
  668.     //{ and synchronize text attributes }
  669.     if (p->drawingOffscreen) 
  670.     {
  671.         SetPort(pWE->offscreenPort);
  672.         TextFont(pAttrs->runStyle.tsFont);
  673.         TextFace(pAttrs->runStyle.tsFace);
  674.         TextSize(pAttrs->runStyle.tsSize);
  675.     } //{ if drawingOffscreen }
  676.  
  677.     //{ get horizontal coordinate of the pen before drawing the segment }
  678.     GetPen((Point *)&segmentRect.top);
  679.  
  680.     //{ set the foreground color }
  681.     if (p->usingColor)
  682.     {
  683.         RGBForeColor(&pAttrs->runStyle.tsColor);
  684.     }
  685.     
  686.     if (pAttrs->runStyle.tsObject != kNullObject)
  687.     {
  688.         // { EMBEDDED OBJECT}
  689.         if (_WEDrawObject((WEObjectDescHandle)(pAttrs->runStyle.tsObject))!=noErr)
  690.         {
  691.             ; // We don't know what to do with errors
  692.         }
  693.     }
  694.     else
  695.     {
  696.         // { REGULAR TEXT }
  697.     
  698.         slop = 0;
  699.  
  700.         //{ calculate the "slop" (extra space) for this text segment (justified text only) }
  701.         if (pWE->alignment == weJustify) 
  702.         {
  703.             //{ if this is the last segment on the line, strip trailing spaces }
  704.             if (!(styleRunPosition & 1))
  705.             {
  706.                 segmentLength = VisibleLength(pSegment, segmentLength);
  707.             }
  708.             //{ calculate how much extra space is to be applied to this text segment }
  709.             slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition, 
  710.                 kOneToOneScaling, kOneToOneScaling), pLine->lineJustAmount);
  711.  
  712.         } //{ if alignment = weJustify }
  713.  
  714. #ifdef WASTE_DEBUG
  715.         _WEAssert(pWE->drawTextHook != NULL, "\pMissing DrawText Hook");
  716. #endif
  717.         //{ draw the segment }
  718.         CallWEDrawTextProc(pSegment, segmentLength, slop, styleRunPosition, p->hWE,
  719.             (WEDrawTextUPP)pWE->drawTextHook);
  720.     }
  721.     
  722.     //{ get horizontal coordinate of the pen after drawing the segment }
  723.     GetPen((Point *)&segmentRect.bottom);
  724.     segmentRect.bottom = p->lineRect.bottom;
  725.  
  726.     //{ if this segment is in the TSM area, underline it in the appropriate way }
  727.     if (BTST(pAttrs->runStyle.tsFlags, tsTSMHilite)) 
  728.     {
  729.         _WEDrawTSMHilite(&segmentRect, pAttrs->runStyle.tsFlags);
  730.     }
  731.     if (p->drawingOffscreen) 
  732.     {
  733.         if (!(styleRunPosition & 1)) 
  734.         {
  735.             //{ after drawing offscreen the last segment, }
  736.             //{ prepare to copy the offscreen buffer to video RAM }
  737.  
  738.             //{ first set the graphics world to the screen port }
  739.             SetGWorld((CGrafPtr)p->screenPort, p->screenDevice);
  740.  
  741.             //{ before calling CopyBits, set the foreground color to black to avoid colorization (color only) }
  742.             if (p->usingColor) 
  743.             {
  744.                 theColorBlack.red = 0;
  745.                 theColorBlack.green = 0;
  746.                 theColorBlack.blue = 0;
  747.                 RGBForeColor(&theColorBlack);
  748.             }
  749.             
  750.             //{ copy the offscreen image of the [visible portion of the] line to the screen }
  751.             CopyBits(&pWE->offscreenPort->portBits, &p->screenPort->portBits, &p->drawRect,
  752.                     &p->drawRect, srcCopy, (RgnHandle)nil);
  753.  
  754.             //{ restore the original offscreen coordinate system and unlock the pixel image }
  755.             SetPort(pWE->offscreenPort);
  756.             SetOrigin(0, 0);
  757.             if (p->usingColor)
  758.                 RGBForeColor(&theColorBlack);    // { this fixes a bug in Style 1.3 }
  759.             UnlockPixels(p->offscreenPixels);
  760.  
  761.         } //{ if last segment }
  762.  
  763.         //{ restore the screen port for _WESegmentLoop }
  764.         SetPort(p->screenPort);
  765.     } //{ if drawingOffscreen }
  766.     return retval;
  767. }
  768.  
  769. pascal void _WEDrawLines (long firstLine, long lastLine, Boolean doErase, WEHandle hWE)
  770. {
  771.     //{ draw the specified range of lines }
  772.     //{ we can safely assume that the WE record is already locked }
  773.     //{ and the port is already set the pWE->port }
  774.  
  775.     WEPtr pWE;
  776.     Rect bounds;                    //{ bounds of the offscreen buffer, in global coordinates }
  777.     Boolean usingColor;                //{ TRUE if we're drawing in color }
  778.     Boolean usingOffscreen;            //{ TRUE if we're using an offscreen port }
  779.     Boolean drawingOffscreen;        //{ TRUE if actually drawing to an offscreen buffer }
  780.     struct SLDrawData callbackData;
  781.     GDHandle screenDevice;
  782.     GrafPtr screenPort;
  783.  
  784.     pWE = *hWE;
  785.     usingOffscreen = false;
  786.     drawingOffscreen = false;
  787.     usingColor = BTST(pWE->flags, weFHasColorQD);
  788.         
  789.     //{ do nothing if our graphics port is not visible }
  790.     if (EmptyRgn(pWE->port->visRgn))
  791.     {
  792.         return;
  793.     }
  794.  
  795.     // { save graphics world }
  796.     GetGWorld((GWorldPtr *)&screenPort, &screenDevice);
  797.  
  798.     //{ If doErase is TRUE, we're drawing over old text, so we must erase each line }
  799.     //{ before redrawing it.  But if the weFDrawOffscreen feature is enabled, we draw }
  800.     //{ the entire line offscreen and  we copy the image right over the old line, }
  801.     //{ without erasing it, thus achieving a very smooth drawing effect. }
  802.  
  803.     if ((doErase) && BTST(pWE->flags, weFDrawOffscreen)) 
  804.     {
  805.         //{ has an offscreen world already been allocated? }
  806.         if (pWE->offscreenPort == nil) 
  807.         {
  808.             GWorldPtr aGWorld; //mf
  809.             //{ nope,  create one; its bounds are set initially to an arbitrary rectangle }
  810.             SetRect(&bounds, 0, 0, 1, 1);
  811.             if (NewGWorld(&aGWorld, 0, &bounds, nil, nil,
  812.                 pixPurge + noNewDevice + useTempMem) != noErr)
  813.             { ; }
  814.             (pWE->offscreenPort)=(GrafPtr)aGWorld; //mf
  815.         }
  816.         usingOffscreen = (pWE->offscreenPort != nil);
  817.     }
  818.  
  819.     callbackData.hWE = hWE;
  820.     callbackData.bounds = bounds;
  821.     callbackData.usingColor = usingColor;
  822.     callbackData.usingOffscreen = usingOffscreen;
  823.     callbackData.drawingOffscreen = drawingOffscreen;
  824.     callbackData.doErase = doErase;
  825.     SetRect(&callbackData.lineRect,0,0,0,0);                //mf
  826.     SetRect(&callbackData.drawRect,0,0,0,0);                //mf
  827.     callbackData.offscreenPixels=NULL;                         //
  828.     callbackData.screenPort = screenPort;
  829.     callbackData.screenDevice = screenDevice;
  830.  
  831.     _WESegmentLoop(firstLine, lastLine, SLDraw, (void *) &callbackData, hWE);
  832.  
  833.     // { restore graphics world }
  834.     SetGWorld((GWorldPtr)screenPort, screenDevice);
  835.  
  836. }
  837.  
  838. pascal short _WECalcPenIndent(short slop, short alignment)
  839. {
  840.     short retval;
  841.  
  842.     //{ if alignment is weFlushDefault, use the system global SysDirection }
  843.     if (alignment == weFlushDefault) 
  844.     {
  845.         if (GetSysDirection() == 0)
  846.         { 
  847.             alignment = weFlushLeft;
  848.         }
  849.         else
  850.         {
  851.             alignment = weFlushRight;
  852.         }
  853.     }
  854.     if (alignment == weFlushRight) 
  855.     {
  856.         retval = slop;                                //{ right aligned }
  857.     }
  858.     else if (alignment == weCenter) 
  859.     {
  860.         retval = slop / 2;                        //{ centered }
  861.     }
  862.     else
  863.     {
  864.         retval = 0;                                    //{ left aligned or justified }
  865.     }
  866.     return retval;
  867. }
  868.  
  869. pascal void _WESaveQDEnvironment(GrafPtr port, Boolean saveColor, QDEnvironment *theEnvironment)
  870. {
  871.     GetPort(&theEnvironment->envPort);
  872.     SetPort(port);
  873.     GetPenState(&theEnvironment->envPen);
  874.     PenNormal();
  875.     theEnvironment->envStyle.tsFont = port->txFont;
  876.     theEnvironment->envStyle.tsFace = ((GrafPtr1)port)->txFace;
  877.     theEnvironment->envStyle.tsFlags = saveColor;        //{ remember if color was saved }
  878.     theEnvironment->envStyle.tsSize = port->txSize;
  879.     if (saveColor) 
  880.     {
  881.         GetForeColor(&theEnvironment->envStyle.tsColor);
  882.     }
  883.     theEnvironment->envMode = port->txMode;
  884.     TextMode(srcOr);
  885. }
  886.  
  887. pascal void _WERestoreQDEnvironment(QDEnvironment *theEnvironment)
  888. {
  889.     SetPenState(&theEnvironment->envPen);
  890.     TextFont(theEnvironment->envStyle.tsFont);
  891.     TextFace(theEnvironment->envStyle.tsFace);
  892.     TextSize(theEnvironment->envStyle.tsSize);
  893.     TextMode(theEnvironment->envMode);
  894.     if (theEnvironment->envStyle.tsFlags) 
  895.     {
  896.         RGBForeColor(&theEnvironment->envStyle.tsColor);
  897.     }
  898.     SetPort(theEnvironment->envPort);
  899. }
  900.  
  901. pascal void _WEFillFontInfo (GrafPtr port, WERunAttributes *targetStyle)
  902. {
  903.     //{ given a WERunAttributes record, fill in the runHeight, runAscent fields etc. }
  904.     FontInfo fInfo;
  905.     QDEnvironment saveEnvironment;
  906.  
  907.     _WESaveQDEnvironment(port, false, &saveEnvironment);
  908.  
  909.     //{ we don't want a zero font size; although QuickDraw accepts zero to mean }
  910.     //{ the default font size, it can cause trouble to us when we do calculations }
  911.     if (targetStyle->runStyle.tsSize == 0) 
  912.     {
  913.         targetStyle->runStyle.tsSize = 12;
  914.     }
  915.     
  916.     //{ set the text attributes }
  917.     TextFont(targetStyle->runStyle.tsFont);
  918.     TextSize(targetStyle->runStyle.tsSize);
  919.     TextFace(targetStyle->runStyle.tsFace);
  920.     GetFontInfo(&fInfo);
  921.     targetStyle->runHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
  922.     targetStyle->runAscent = fInfo.ascent;
  923.     _WERestoreQDEnvironment(&saveEnvironment);
  924. }
  925.  
  926. pascal void _WECopyStyle(WETextStyle *sourceStyle, WETextStyle *targetStyle, short offStyles,
  927.         short mode)
  928. {
  929.     //{ Copy some or all of the attributes composing sourceStyle to targetStyle. }
  930.     //{ The mode parameter determines which attributes are to be copied and how. }
  931.     //{ If mode contains weDoToggleFace,  offStyles indicates which }
  932.     //{ QuickDraw styles are to be turned off. }
  933.  
  934.     long longMode;
  935.     long longSize;
  936.     long sourceFace, targetFace;
  937.  
  938.     longMode = mode;    //{ this allows the compiler to generate tighter code }
  939.  
  940.     //{ if the kModeFont bit is set, copy the font family number }
  941.     if (BTST(longMode, kModeFont))
  942.     { 
  943.             targetStyle->tsFont = sourceStyle->tsFont;
  944. #ifdef WASTE_RESOLVE_FONT_DESIGNATORS
  945.             if (targetStyle->tsFont == systemFont)
  946.                 targetStyle->tsFont = GetSysFont();
  947.             if (targetStyle->tsFont == applFont)
  948.                 targetStyle->tsFont = GetAppFont();
  949. #endif
  950.     }
  951.     
  952.     //{ if the kModeSize or the kModeAddSize bit is set, alter the font size }
  953.     if ((longMode & (weDoSize + weDoAddSize)) != 0) 
  954.     {
  955.         // { copy size to a long variable to avoid integer overflows when doing additions }
  956.         longSize = sourceStyle->tsSize;
  957.  
  958.         // { zero really means 12 }
  959.         if (longSize == 0)
  960.             longSize = 12;
  961.  
  962.         //{ if kModeAddSize is set, the source size is added to the target size, }
  963.         //{ otherwise the source size replaces the target size outright }
  964.         if (BTST(longMode, kModeAddSize)) 
  965.         {
  966.             longSize = longSize + targetStyle->tsSize;
  967.         }
  968.         //{ range-check the resulting size }
  969.         longSize = _WEPinInRange(longSize, kMinFontSize, kMaxFontSize);
  970.         targetStyle->tsSize = longSize;
  971.     } //{ if alter size }
  972.  
  973.     //{ if kModeFace is set, copy the QuickDraw styles (tsFace field); }
  974.     //{ the (rather complex) rules for copying the styles are explained below in detail }
  975.     if (BTST(longMode, kModeFace)) 
  976.     {
  977.         sourceFace = sourceStyle->tsFace;
  978.         targetFace = targetStyle->tsFace;
  979.     
  980.         //{ sourceFace replaces targetFace outright if one or both of these conditions hold: }
  981.         //{ 1. sourceFace is zero (= empty set = plain text) }
  982.         //{ 2. the kModeReplaceFace bit is set }
  983.     
  984.         if ((sourceFace == 0) || BTST(longMode, kModeReplaceFace)) 
  985.         {
  986.             targetFace = sourceFace;
  987.         }
  988.         else
  989.         {
  990.             //{ Otherwise sourceFace is interpreted as a bitmap indicating }
  991.             //{ which styles are to be altered -- all other styles are left intact. }
  992.             //{ What exactly happens to the styles indicated in sourceFace }
  993.             //{ depends on whether the kModeToggleFace bit is set or clear. }
  994.     
  995.             //{ if kModeToggleFace is set, turn a style off if it's set in offStyles, else turn it on }
  996.             if (BTST(longMode, kModeToggleFace)) 
  997.             {
  998.                 targetFace = (sourceFace ^ offStyles) | (targetFace & (~sourceFace));
  999.             }
  1000.             else
  1001.             {
  1002.                 //{ if kModeToggleFace is clear, turn on the styles specified in sourceStyle }
  1003.                 targetFace = targetFace | sourceFace;
  1004.             }
  1005.             //{ the condense and extend attributes are mutually exclusive: if one is set }
  1006.             //{ in sourceFace, remove it from targetFace }
  1007.             if (BTST(sourceFace, tsCondense))
  1008.             { 
  1009.                 BCLR(targetFace, tsExtend);
  1010.             }
  1011.             else if (BTST(sourceFace, tsExtend)) 
  1012.             {
  1013.                 BCLR(targetFace, tsCondense);
  1014.             }
  1015.         }
  1016.         targetStyle->tsFace = targetFace;
  1017.     }  //{ if alter face }
  1018.  
  1019.     //{ if kModeColor is set, change target color }
  1020.     if (BTST(longMode, kModeColor)) 
  1021.     {
  1022.         targetStyle->tsColor = sourceStyle->tsColor;
  1023.     }
  1024.     
  1025.     //{ if kModeObject is set, copy object descriptor }
  1026.     if (BTST(longMode, kModeObject))
  1027.     {
  1028.         targetStyle->tsObject = sourceStyle->tsObject;
  1029.     }
  1030.  
  1031.     //{ always clear targetStyle.tsFlags by default }
  1032.     targetStyle->tsFlags = 0;
  1033.  
  1034.     //{ if kModeFlags is set, copy the tsFlags field }
  1035.     if (BTST(longMode, kModeFlags))
  1036.     { 
  1037.         targetStyle->tsFlags = sourceStyle->tsFlags;
  1038.     }
  1039. }
  1040.  
  1041. pascal Boolean _WEOffsetInRange(long offset, char edge, long rangeStart, long rangeEnd)
  1042. {
  1043.     //{ return TRUE if the position specified by the pair < offset, edge > }
  1044.     //{ is within the specified range }
  1045.  
  1046.     //{ if edge is kTrailingEdge, offset really refers to the preceding character }
  1047.     if (edge == kTrailingEdge) 
  1048.     {
  1049.         offset = offset - 1;
  1050.     }
  1051.     //{ return TRUE iff offset is within the specified range }
  1052.     return ((offset >= rangeStart) && (offset < rangeEnd));
  1053. }
  1054.  
  1055.  
  1056.  
  1057.